
#define pr_fmt(fmt) KBUILD_MODNAME ": %s: " fmt, __func__

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/device.h>
#include <linux/io.h>

static struct kobject *k_obj = NULL;

char test_buff[32];
static ssize_t sysfs_demo_show_test(struct kobject *kobj, struct kobj_attribute *attr, char * buf)
{
	return sprintf(buf, "%s\n", test_buff);
}
static ssize_t sysfs_demo_store_test(struct kobject *kobj, struct kobj_attribute *attr, const char * buf, size_t count)
{
	sprintf(test_buff, "%s", buf);
	return count;
}

static ssize_t sysfs_demo_show_regctl(struct kobject *kobj, struct kobj_attribute *attr, char * buf)
{
	return sprintf(buf, "help:\necho \"read addr\" or echo \"write addr value\"\n\n");
}

static ssize_t sysfs_demo_store_regctl(struct kobject *kobj, struct kobj_attribute *attr, const char * buf, size_t count)
{
	char ctl[16];
	__u32 addr = 0;
	__u32 val = 0;
	void __iomem *map_addr;

	memset(ctl, 0, sizeof(ctl));

	if(buf[0] == 'r')
	{
		sscanf(buf, "%s %x", ctl, &addr);
		printk("ctl:%s addr:0x%x\n", ctl, addr);
		map_addr = ioremap(addr, 4);
		if(!map_addr)
		{
			printk("can not map for read reg:0x%x\n", addr);
			return 0;
		}
		val = __raw_readl(map_addr);
		iounmap(map_addr);
		printk("val:0x%x\n", val);
		return count;
		
	}
	else if(buf[0] == 'w')
	{
		sscanf(buf, "%s %x %x", ctl, &addr, &val);
		printk("ctl:%s addr:0x%x val:0x%x\n", ctl, addr, val);
		map_addr = ioremap(addr, 4);
		if(!map_addr)
		{
			printk("can not map for write reg:0x%x\n", addr);
			return 0;
		}
		__raw_writel(val, map_addr);
		iounmap(map_addr);
		return count;
	}
	else
	{
		printk("unspport cmd\n""help:\necho \"read addr\" or echo \"write addr value\"");
		return 0;
	}
	
	return 0;
}

static struct kobj_attribute regctl_attribute =
    __ATTR(regctl, S_IWUSR|S_IRUGO, sysfs_demo_show_regctl, sysfs_demo_store_regctl);
static struct kobj_attribute test_attribute =
    __ATTR(test, S_IWUSR|S_IRUGO, sysfs_demo_show_test, sysfs_demo_store_test);


static struct attribute *sysfs_demo_attributes[] = {
    &regctl_attribute.attr,
    &test_attribute.attr,
    NULL
};

static const struct attribute_group sysfs_demo_attr_group = {
    .attrs = sysfs_demo_attributes,
};


static int __init sysfs_demo_init(void)
{
    if ((k_obj = kobject_create_and_add("imx6ul_regctl", NULL)) == NULL ) {
        pr_err("sysfs_demo sys node create error \n");
        goto out;
    }

    if(sysfs_create_group(k_obj, &sysfs_demo_attr_group) ) {
        pr_err("sysfs_create_group failed\n");
        goto out2;
    }

    pr_info("enter.\n");
    return 0;
	
out3:
    sysfs_remove_group(k_obj, &sysfs_demo_attr_group);
out2:
    kobject_put(k_obj);
out:
    return -1;
}
module_init(sysfs_demo_init);

static void __exit sysfs_demo_exit(void)
{
    pr_info("enter.\n");

    if (k_obj) {
        sysfs_remove_group(k_obj, &sysfs_demo_attr_group);
        kobject_put(k_obj);
    }

}
module_exit(sysfs_demo_exit);

MODULE_LICENSE("GPL");
